﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;

namespace bomberbide
{
    public class gameManager
    {
        #region properties
        private Random maxicul;
        private KeyboardState LastKeyboardState;

        private List<bomber> _Bombers;
        public List<bomber> Bombers
        {
            get { return _Bombers; }
            set { _Bombers = value; }
        }
        
        private ContentManager _Content;
        public ContentManager Content
        {
            get { return _Content; }
            set { _Content = value; }
        }
        
        private List<acteur> _Acteurs;
        public List<acteur> Acteurs
        {
            get { return _Acteurs; }
            set { _Acteurs = value; }
        }
        
        private camera _Camera;
	    public camera Camera
	{
		get { return _Camera;}
		set { _Camera = value;}
	}
	
        private Texture2D _Pierre;
        public Texture2D Pierre
        {
            get { return _Pierre; }
            set { _Pierre = value; }
        }

        private Texture2D _Mur;
        public Texture2D Mur
        {
            get { return _Mur; }
            set { _Mur = value; }
        }

        private Texture2D _Grosmur;
        public Texture2D Grosmur
        {
            get { return _Grosmur; }
            set { _Grosmur = value; }
        }

        private Texture2D _Explosion;
        public Texture2D Explosion
        {
            get { return _Explosion; }
            set { _Explosion = value; }
        }

        private Texture2D _Sol;
        public Texture2D Sol
        {
            get { return _Sol; }
            set { _Sol = value; }
        }

        private typeCase[,] _map;
        public typeCase[,] map
        {
            get { return _map; }
            set { _map = value; }
        }
        #endregion

        private bool Collide(acteur Acteur)
        {
            bool retour = false;
            if ((this.map[(int)((Acteur.Position.X + 10) / 64), (int)((Acteur.Position.Y + 10) / 64)] != typeCase.sol)
                || (this.map[(int)((Acteur.Position.X + 10) / 64), (int)((Acteur.Position.Y + 54) / 64)] != typeCase.sol)
                || (this.map[(int)((Acteur.Position.X + 54) / 64), (int)((Acteur.Position.Y + 10) / 64)] != typeCase.sol)
                || (this.map[(int)((Acteur.Position.X + 54) / 64), (int)((Acteur.Position.Y + 54) / 64)] != typeCase.sol)
                || this.Acteurs.Where(
                    b => b.GetType() == typeof(bombe)
                    && b != this.Bombers.First().LastBomb
                    && new Rectangle((int)Acteur.Position.X + 16, (int)Acteur.Position.Y + 16, 32, 32).Intersects(new Rectangle((int)b.Position.X, (int)b.Position.Y, 64, 64))
                    ).Count() > 0)
                retour = true;

            return retour;
        }

        public void Update(GameTime gameTime)
        {
            List<acteur> toDelete = new List<acteur>();
            List<acteur> toAdd = new List<acteur>();
            #region Inputs
            if (Keyboard.GetState().IsKeyDown(Keys.Q))
            {
                this.Acteurs.First().Position.X -= (float)this.Bombers.First().Speed;
                if (this.Collide(this.Acteurs.First()))
                    this.Acteurs.First().Position.X += (float)this.Bombers.First().Speed;
            }
            if (Keyboard.GetState().IsKeyDown(Keys.D))
            {
                this.Acteurs.First().Position.X += (float)this.Bombers.First().Speed;
                if (this.Collide(this.Acteurs.First()))
                    this.Acteurs.First().Position.X -= (float)this.Bombers.First().Speed;
            }
            if (Keyboard.GetState().IsKeyDown(Keys.Z))
            {
                this.Acteurs.First().Position.Y -= (float)this.Bombers.First().Speed;
                if (this.Collide(this.Acteurs.First()))
                    this.Acteurs.First().Position.Y += (float)this.Bombers.First().Speed;
            }
            if (Keyboard.GetState().IsKeyDown(Keys.S))
            {
                this.Acteurs.First().Position.Y += (float)this.Bombers.First().Speed;
                if (this.Collide(this.Acteurs.First()))
                    this.Acteurs.First().Position.Y -= (float)this.Bombers.First().Speed;
            }

            // Placage bombe
            if (Keyboard.GetState().IsKeyDown(Keys.Space) && this.LastKeyboardState.IsKeyUp(Keys.Space))
            {
                Vector2 posBaboum = new Vector2((float)(Math.Round(this.Acteurs.First().Position.X / 64) * 64), (float)(Math.Round(this.Acteurs.First().Position.Y / 64) * 64));
                if (this.Acteurs.Where(b => b.GetType() == typeof(bombe) && b.Position.X == posBaboum.X && b.Position.Y == posBaboum.Y).Count() == 0
                    && this.Bombers.First().MaxBombe > this.Acteurs.Where(b => b.GetType() == typeof(bombe)).Count())
                {
                    bombe baboum = new bombe(gameTime, this.Content);
                    baboum.Position = posBaboum;
                    baboum.Lanceur = this.Bombers.First();
                    this.Bombers.First().LastBomb = baboum;
                    baboum.Portee = this.Bombers.First().PuissanceBombe;
                    this.Acteurs.Add(baboum);
                }
            }

            this.LastKeyboardState = Keyboard.GetState();
            #endregion
            #region Ticks bombes boums
            foreach (bombe item in this.Acteurs.Where(b => b.GetType() == typeof(bombe)))
            {
                if (item.Boum < gameTime.TotalGameTime.TotalMilliseconds)
                {
                    // Suppression de la bombe qui explose
                    toDelete.Add(item);
                    
                    Vector2 centreExplo = new Vector2((float)(Math.Round(item.Position.X / 64) * 64), (float)(Math.Round(item.Position.Y / 64) * 64));
                    // Centre
                    explosion tempExplo = new explosion(gameTime, this.Content);
                    tempExplo.Position = centreExplo;

                    #region Propagation des explosions
                    Vector2 posCentreExplo = new Vector2((int)Math.Round(item.Position.X / 64), (int)Math.Round(item.Position.Y / 64));
                    #region Haut
                    for (int i = 0; i < item.Portee; i++)
                    {
                        if (this.map[(int)posCentreExplo.X, (int)posCentreExplo.Y - i] == typeCase.sol)
                        {
                            #region Création de l'explosion
                            tempExplo = new explosion(gameTime, this.Content);
                            tempExplo.Position.X = posCentreExplo.X  * 64;
                            tempExplo.Position.Y = (posCentreExplo.Y - i) * 64;
                            toAdd.Add(tempExplo);
                            #endregion
                            #region Déclenchement de la bombe sur cette case
                            foreach (bombe dabombe in this.Acteurs.Where(
                                b => b.GetType() == typeof(bombe)
                                    && b.Position.X == posCentreExplo.X * 64
                                    && b.Position.Y == (posCentreExplo.Y - i) * 64))
                            {
                                dabombe.Boum = 0;
                            }
                            #endregion
                        }
                        else if (this.map[(int)posCentreExplo.X, (int)posCentreExplo.Y - i] == typeCase.mur)
                        {
                            tempExplo = new explosion(gameTime, this.Content);
                            tempExplo.Position.X = posCentreExplo.X * 64;
                            tempExplo.Position.Y = (posCentreExplo.Y - i) * 64;
                            toAdd.Add(tempExplo);
                            this.map[(int)posCentreExplo.X, (int)posCentreExplo.Y - i] = typeCase.sol;
                            #region Proc powerup
                            if (this.Bombers.First().Randomer.Next(1, 4) == 1)
                            {
                                powerup toSpawn = powerup.Generate(this.Bombers.First().Randomer, this.Content);
                                toSpawn.Position = new Vector2(posCentreExplo.X * 64, (posCentreExplo.Y - i) * 64);
                                toAdd.Add(toSpawn);
                            }
                            #endregion
                            break;
                        }
                        else if (this.map[(int)posCentreExplo.X, (int)posCentreExplo.Y - i] == typeCase.grosmur)
                        {
                            this.map[(int)posCentreExplo.X, (int)posCentreExplo.Y - i] = typeCase.mur;
                            break;
                        }
                        else if (this.map[(int)posCentreExplo.X, (int)posCentreExplo.Y - i] == typeCase.incassable)
                        {
                            break;
                        }
                    }
                    #endregion
                    #region Bas
                    for (int i = 0; i < item.Portee; i++)
                    {
                        if (this.map[(int)posCentreExplo.X, (int)posCentreExplo.Y + i] == typeCase.sol)
                        {
                            #region Création de l'explosion
                            tempExplo = new explosion(gameTime, this.Content);
                            tempExplo.Position.X = posCentreExplo.X * 64;
                            tempExplo.Position.Y = (posCentreExplo.Y + i) * 64;
                            toAdd.Add(tempExplo);
                            #endregion
                            #region Déclenchement de la bombe sur cette case
                            foreach (bombe dabombe in this.Acteurs.Where(
                                b => b.GetType() == typeof(bombe)
                                    && b.Position.X == posCentreExplo.X * 64
                                    && b.Position.Y == (posCentreExplo.Y + i) * 64))
                            {
                                dabombe.Boum = 0;
                            }
                            #endregion
                        }
                        else if (this.map[(int)posCentreExplo.X, (int)posCentreExplo.Y + i] == typeCase.mur)
                        {
                            tempExplo = new explosion(gameTime, this.Content);
                            tempExplo.Position.X = posCentreExplo.X * 64;
                            tempExplo.Position.Y = (posCentreExplo.Y + i) * 64;
                            toAdd.Add(tempExplo);
                            this.map[(int)posCentreExplo.X, (int)posCentreExplo.Y + i] = typeCase.sol;
                            #region Proc powerup
                            if (this.Bombers.First().Randomer.Next(1, 4) == 1)
                            {
                                powerup toSpawn = powerup.Generate(this.Bombers.First().Randomer, this.Content);
                                toSpawn.Position = new Vector2(posCentreExplo.X * 64, (posCentreExplo.Y + i) * 64);
                                toAdd.Add(toSpawn);
                            }
                            #endregion
                            break;
                        }
                        else if (this.map[(int)posCentreExplo.X, (int)posCentreExplo.Y + i] == typeCase.grosmur)
                        {
                            this.map[(int)posCentreExplo.X, (int)posCentreExplo.Y + i] = typeCase.mur;
                            break;
                        }
                        else if (this.map[(int)posCentreExplo.X, (int)posCentreExplo.Y + i] == typeCase.incassable)
                        {
                            break;
                        }
                    }
                    #endregion
                    #region Gauche
                    for (int i = 0; i < item.Portee; i++)
                    {
                        if (this.map[(int)posCentreExplo.X - i, (int)posCentreExplo.Y] == typeCase.sol)
                        {
                            #region Création de l'explosion
                            tempExplo = new explosion(gameTime, this.Content);
                            tempExplo.Position.X = (posCentreExplo.X - i) * 64;
                            tempExplo.Position.Y = posCentreExplo.Y * 64;
                            toAdd.Add(tempExplo);
                            #endregion
                            #region Déclenchement de la bombe sur cette case
                            foreach (bombe dabombe in this.Acteurs.Where(
                                b => b.GetType() == typeof(bombe)
                                    && b.Position.X == (posCentreExplo.X - i) * 64
                                    && b.Position.Y == posCentreExplo.Y * 64))
                            {
                                dabombe.Boum = 0;
                            }
                            #endregion
                        }
                        else if (this.map[(int)posCentreExplo.X - i, (int)posCentreExplo.Y] == typeCase.mur)
                        {
                            tempExplo = new explosion(gameTime, this.Content);
                            tempExplo.Position.X = (posCentreExplo.X - i) * 64;
                            tempExplo.Position.Y = posCentreExplo.Y * 64;
                            toAdd.Add(tempExplo);
                            this.map[(int)posCentreExplo.X - i, (int)posCentreExplo.Y] = typeCase.sol;
                            #region Proc powerup
                            if (this.Bombers.First().Randomer.Next(1, 4) == 1)
                            {
                                powerup toSpawn = powerup.Generate(this.Bombers.First().Randomer, this.Content);
                                toSpawn.Position = new Vector2((posCentreExplo.X - i) * 64, posCentreExplo.Y * 64);
                                toAdd.Add(toSpawn);
                            }
                            #endregion
                            break;
                        }
                        else if (this.map[(int)posCentreExplo.X - i, (int)posCentreExplo.Y] == typeCase.grosmur)
                        {
                            this.map[(int)posCentreExplo.X - i, (int)posCentreExplo.Y] = typeCase.mur;
                            break;
                        }
                        else if (this.map[(int)posCentreExplo.X - i, (int)posCentreExplo.Y] == typeCase.incassable)
                        {
                            break;
                        }
                    }
                    #endregion
                    #region Droite
                    for (int i = 0; i < item.Portee; i++)
                    {
                        if (this.map[(int)posCentreExplo.X + i, (int)posCentreExplo.Y] == typeCase.sol)
                        {
                            #region Création de l'explosion
                            tempExplo = new explosion(gameTime, this.Content);
                            tempExplo.Position.X = (posCentreExplo.X + i) * 64;
                            tempExplo.Position.Y = posCentreExplo.Y * 64;
                            toAdd.Add(tempExplo);
                                                        #endregion
                            #region Déclenchement de la bombe sur cette case
                            foreach (bombe dabombe in this.Acteurs.Where(
                                b => b.GetType() == typeof(bombe)
                                    && b.Position.X == (posCentreExplo.X + i) * 64
                                    && b.Position.Y == posCentreExplo.Y * 64))
                            {
                                dabombe.Boum = 0;
                            }
                            #endregion
                        }
                        else if (this.map[(int)posCentreExplo.X + i, (int)posCentreExplo.Y] == typeCase.mur)
                        {
                            tempExplo = new explosion(gameTime, this.Content);
                            tempExplo.Position.X = (posCentreExplo.X + i) * 64;
                            tempExplo.Position.Y = posCentreExplo.Y * 64;
                            toAdd.Add(tempExplo);
                            this.map[(int)posCentreExplo.X + i, (int)posCentreExplo.Y] = typeCase.sol;
                            #region Proc powerup
                            if (this.Bombers.First().Randomer.Next(1, 4) == 1)
                            {
                                powerup toSpawn = powerup.Generate(this.Bombers.First().Randomer, this.Content);
                                toSpawn.Position = new Vector2((posCentreExplo.X + i) * 64, (posCentreExplo.Y) * 64);
                                toAdd.Add(toSpawn);
                            }
                            #endregion
                            break;
                        }
                        else if (this.map[(int)posCentreExplo.X + i, (int)posCentreExplo.Y] == typeCase.grosmur)
                        {
                            this.map[(int)posCentreExplo.X + i, (int)posCentreExplo.Y] = typeCase.mur;
                            break;
                        }
                        else if (this.map[(int)posCentreExplo.X + i, (int)posCentreExplo.Y] == typeCase.incassable)
                        {
                            break;
                        }
                    }
                    #endregion
                    #endregion
                }
            }
            #endregion
            #region Ticks explosions boums
            foreach (explosion item in this.Acteurs.Where(b => b.GetType() == typeof(explosion)))
            {
                // Test fin de l'explosion
                if (item.Mort < gameTime.TotalGameTime.TotalMilliseconds)
                {
                    toDelete.Add(item);
                }
            }
            foreach (var item in toDelete)
                this.Acteurs.Remove(item);
            #endregion
            #region Gestion des bombers
            foreach (bomber item in this.Bombers)
            {
                
            }
            #endregion
            #region Gestion du ramassage des powerups
            foreach (joueur item in this.Acteurs.Where(b => b.GetType() == typeof(joueur)))
            {
                foreach (powerup item_ in this.Acteurs.Where(
                    b => new Rectangle((int)item.Position.X, (int)item.Position.Y, 64, 64).Intersects(
                        new Rectangle((int)b.Position.X + 16, (int)b.Position.Y + 16, 32, 32))
                        && b.GetType().IsSubclassOf(typeof(powerup))))
                {
                    item_.Modif(this.Bombers.First(), gameTime);
                    toDelete.Add(item_);                                                                                                                                    
                }
            }
            #endregion
            foreach (var item in this.Bombers)
                item.Update(gameTime);
            foreach (var item in toAdd)
                this.Acteurs.Add(item);
            foreach (var item in toDelete)
                this.Acteurs.Remove(item);
        }
        public void Draw()
        {
            #region Draw de la map
            for (int i = 0; i < 15; i++)
            {
                for (int j = 0; j < 13; j++)
                {
                    switch (this.map[i, j])
                    {
                        case typeCase.sol:
                            this.Camera.Draw(this.Sol, new Rectangle(64 * i, 64 * j, 64, 64), Color.White);
                            break;
                        case typeCase.mur:
                            this.Camera.Draw(this.Mur, new Rectangle(64 * i, 64 * j, 64, 64), Color.White);
                            break;
                        case typeCase.grosmur:
                            this.Camera.Draw(this.Grosmur, new Rectangle(64 * i, 64 * j, 64, 64), Color.White);
                            break;
                        case typeCase.incassable:
                            this.Camera.Draw(this.Pierre, new Rectangle(64 * i, 64 * j, 64, 64), Color.White);
                            break;
                        default:
                            break;
                    }
                }
            #endregion
                #region Draw des buffs
                int x = 0;
                foreach (var item in this.Bombers)
                {
                    foreach (var item_ in item.Buffs)
                    {
                        this.Camera.Draw(item_.Texture, new Rectangle(5 + 32 * x, 5, 32, 32), Color.White);
                        x++;
                    }   
                }
                #endregion
            }

            foreach (var item in this.Acteurs)
                item.Draw(this.Camera);
        }
        #region Constructeurs
        public gameManager(camera Camera)
        {
            this.map = new typeCase[15, 13];
            this.Camera = Camera;
            this.maxicul = new Random();
            this.Acteurs = new List<acteur>();
            this.Bombers = new List<bomber>();

            #region Génération Map
            // Bords extérieurs
            for (int i = 0; i < 15; i++)
            {
                this.map[i, 0] = typeCase.incassable;
                this.map[i, 12] = typeCase.incassable;
            }
            for (int i = 0; i < 13; i++)
            {
                this.map[0, i] = typeCase.incassable;
                this.map[14, i] = typeCase.incassable;
            }
            // Remplissage random
            for (int i = 1; i < 13; i++)
            {
                for (int j = 1; j < 12; j++)
                {
                    int proc = this.maxicul.Next(1, 100);

                    this.map[i, j] = typeCase.sol;
                    if (proc > 30)
                        this.map[i, j] = typeCase.mur;
                    if (proc > 90)
                        this.map[i, j] = typeCase.grosmur;
                }
            }
            // "poteaux"
            for (int i = 0; i < 6; i++)
            {
                for (int j = 0; j < 5; j++)
                {
                    this.map[2 + (i * 2), 2 + j * 2] = typeCase.incassable;
                }
            }
            // Clearage des spawns
            #region clearage des spawns
            this.map[1, 1] = typeCase.sol;
            this.map[2, 1] = typeCase.sol;
            this.map[1, 2] = typeCase.sol;

            this.map[12, 1] = typeCase.sol;
            this.map[13, 1] = typeCase.sol;
            this.map[13, 2] = typeCase.sol;

            this.map[1, 11] = typeCase.sol;
            this.map[2, 11] = typeCase.sol;
            this.map[1, 10] = typeCase.sol;

            this.map[13, 11] = typeCase.sol;
            this.map[12, 11] = typeCase.sol;
            this.map[13, 10] = typeCase.sol;
            #endregion
            #endregion
        }
        public gameManager(camera Camera, ContentManager Content) : this(Camera)
        {
            this.Mur = Content.Load<Texture2D>("sprite/brique");
            this.Explosion = Content.Load<Texture2D>("sprite/explosion");
            this.Pierre = Content.Load<Texture2D>("sprite/pierre");
            this.Sol = Content.Load<Texture2D>("sprite/sol");
            this.Grosmur = Content.Load<Texture2D>("sprite/grosmur");

            this.Content = Content;

            bomber myBomber = new bomber(Content);
            this.Bombers.Add(myBomber);
            this.Acteurs.Add(myBomber.Joueur);
        }
        #endregion
        #region Enums
        public enum typeCase
        {
            sol, mur, grosmur, incassable
        }
        #endregion
    }
}
